#include "GMPInt.h"
#include <stdexcept>

#include "../DEBUG_PRINT_MACRO.h"

#ifdef DEBUG_PRINTS_LEVEL_1
#include <iostream>
#endif


// 构造函数与析构函数
GMPInt::GMPInt() {
// #ifdef DEBUG_PRINTS_LEVEL_3
//     std::cout << "GMPInt constructor called" << std::endl;
// #endif
    mpz_init(value);
}

GMPInt::GMPInt(int src) {
    mpz_init_set_si(value, src);
}


GMPInt::GMPInt(const mpz_t src) {
    mpz_init_set(value, src);
}

GMPInt::GMPInt(const GMPInt& other) {
    mpz_init_set(value, other.value);
}

GMPInt::GMPInt(GMPInt&& other) noexcept {
    mpz_init(value);
    mpz_swap(value, other.value);
}

GMPInt::~GMPInt() {
    mpz_clear(value);
}

// 赋值运算符
GMPInt& GMPInt::operator=(const GMPInt& other) {
    if (this != &other) {
        mpz_set(value, other.value);
    }
    return *this;
}

GMPInt& GMPInt::operator=(GMPInt&& other) noexcept {
    if (this != &other) {
        mpz_swap(value, other.value);
    }
    return *this;
}

GMPInt& GMPInt::operator=(mpz_t& other) noexcept {
    mpz_swap(value, other);
    return *this;
}

GMPInt& GMPInt::operator=(mpz_t&& other) noexcept {
    mpz_swap(value, other);
    return *this;
}

GMPInt& GMPInt::operator=(const int other) noexcept {
    mpz_set_si(value, other);
    return *this;
}


// 算术运算符
GMPInt GMPInt::operator+(const GMPInt& other) const {
    GMPInt result;
    mpz_add(result.value, value, other.value);
    return result;
}

GMPInt GMPInt::operator+(const int& other) const {
    GMPInt result;
    mpz_add_ui(result.value, value, other);
    return result;
}

GMPInt GMPInt::operator-(const GMPInt& other) const {
    GMPInt result;
    mpz_sub(result.value, value, other.value);
    return result;
}

GMPInt GMPInt::operator-() const {
    GMPInt result;
    mpz_neg(result.value, value);
    return result;
}

GMPInt GMPInt::operator-(int other) const {
    GMPInt result;
    if (other >= 0) {
        mpz_sub_ui(result.value, value, other);
    } else {
        mpz_add_ui(result.value, value, -other);
    }
    return result;
}

GMPInt operator-(int value, const GMPInt& p) {
    GMPInt tmp;
    mpz_set_si(tmp.value, value);
    return tmp - p;
}

GMPInt operator*(int value, const GMPInt& p){
    GMPInt tmp;
    mpz_set_si(tmp.value, value);
    return tmp * p;
}


GMPInt GMPInt::operator*(const GMPInt& other) const {
    GMPInt result;
    mpz_mul(result.value, value, other.value);
    return result;
}

GMPInt GMPInt::operator*(const int& other) const {
    GMPInt result;
    mpz_mul_si(result.value, value, other);
    return result;
}

GMPInt GMPInt::operator/(const GMPInt& other) const {
    if (mpz_sgn(other.value) == 0) {
        throw std::invalid_argument("Division by zero");
    }
    GMPInt result;
    mpz_div(result.value, value, other.value);
    return result;
}

GMPInt GMPInt::operator/(const int& other) const {
    if (other == 0) {
        throw std::invalid_argument("Division by zero");
    }
    GMPInt result;
    mpz_div_ui(result.value, value, other);
    return result;
}

GMPInt GMPInt::operator%(const GMPInt& other) const {
    GMPInt result;
    mpz_mod(result.value, value, other.value);
    return result;
}

GMPInt GMPInt::operator%(const int& other) const {
    GMPInt result;
    mpz_mod_ui(result.value, value, other);
// #ifdef DEBUG_PRINTS_LEVEL_1
// std::cout<<"GMP Int modulo operation result: "; mpz_out_str(stdout, 10, result.value); std::cout << std::endl;
// #endif
    
    return result;
}

// 复合赋值运算符
GMPInt& GMPInt::operator/=(const int& other) {
    mpz_div_ui(value, value, other);
    return *this;
}

GMPInt& GMPInt::operator%=(const int& other) {
    mpz_mod_ui(value, value, other);
    return *this;
}

// 比较运算符
bool GMPInt::operator==(const GMPInt& other) const {
    return mpz_cmp(value, other.value) == 0;
}

bool GMPInt::operator==(const int& other) const {
    return mpz_cmp_ui(value, other) == 0;
}

bool GMPInt::operator<(const GMPInt& other) const {
    return mpz_cmp(value, other.value) < 0;
}

bool GMPInt::operator<(const int& other) const {
    return mpz_cmp_ui(value, other) < 0;
}

bool GMPInt::operator>(const GMPInt& other) const {
    return mpz_cmp(value, other.value) > 0;
}

bool GMPInt::operator>(const int& other) const {
    return mpz_cmp_ui(value, other) > 0;
}

bool GMPInt::operator<=(const int& other) const{
    return mpz_cmp_ui(value, other) <= 0;
}
bool GMPInt::operator>=(const GMPInt& other) const{
    return mpz_cmp(value, other.value) >= 0;
}

// 类型转换运算符
GMPInt::operator const mpz_t&() const { return value; }
GMPInt::operator mpz_t&() { return value; }



GMPInt GMPInt::powm(const GMPInt& exp, const GMPInt& mod){
    GMPInt result;
    mpz_powm(result, this->value, exp, mod);
    return result;
}

std::string GMPInt::to_str() const{
    char* str1 = mpz_get_str(nullptr, 10, this->value);
    std::string result(str1);
    free(str1);
    return result;
}

int GMPInt::set_by_str(const std::string& str) {
    int result = mpz_set_str(this->value, str.c_str(), 10);
    if (result != 0) {
        throw std::invalid_argument("Invalid string representation of GMPInt");
    }
    return result;
}


// 非成员比较运算符实现
bool operator!=(const GMPInt& lhs, const GMPInt& rhs) { return !(lhs == rhs); }

